package com.nowcoder.community.service;

import com.nowcoder.community.dao.elasticsearch.DiscussPostRepository;
import com.nowcoder.community.entity.DiscussPost;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class ElasticsearchService {

    @Autowired
    private DiscussPostRepository discussRepository;

    @Autowired
    private ElasticsearchTemplate elasticTemplate;


    // 保存一个帖子到es
    public void saveDiscussPost(DiscussPost discussPost){
        discussRepository.save(discussPost);
    }

    // 删除一个帖子到es
    public void deleteDiscussPost(int id){
        discussRepository.deleteById(id);
    }

    /**
     * 搜索
     * @param keyWord : 关键字
     * @param current : 当前是第几页，从0开始
     * @param limit : 每页大小
     * @return Page<DiscussPost> : es提供的封装结果的实体
     */
    public Page<DiscussPost> searchDiscussPost(String keyWord,int current,int limit){
        // ----------------------- 构造搜索条件 -----------------------
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                // .withQuery((QueryBuilders.multiMatchQuery("关键字","字段1","字段2"))构造搜索条件
                .withQuery(QueryBuilders.multiMatchQuery(keyWord,"title","content"))
                // .withSort(SortBuilders.fieldSort("排序字段").order(排序方式))构造排序条件
                // type 1 : 置顶
                .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                // score : 高分加精
                .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                // 添加分页条件 withPageable(PageRequest.of(起始页[从0开始],每页条数))
                .withPageable(PageRequest.of(current,limit))
                // 高亮显示
                .withHighlightFields(
                        // css中将<em>高亮为红色
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                        // .build()构造查询器
                ).build();
        // ----------------------- 返回结果 -----------------------
        return elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                // 得到命中的数据
                SearchHits hits = response.getHits();
                if (hits.getTotalHits()<=0){
                    return null;
                }
                List<DiscussPost> list = new ArrayList<>();
                // hit是封装json格式的数据map
                for (SearchHit hit : hits) {
                    DiscussPost post = new DiscussPost();
                    String id = hit.getSourceAsMap().get("id").toString();
                    post.setId(Integer.valueOf(id));
                    String userId = hit.getSourceAsMap().get("userId").toString();
                    post.setUserId(Integer.valueOf(userId));

                    // 原始的title
                    String title = hit.getSourceAsMap().get("title").toString();
                    post.setTitle(title);

                    // 原始的content
                    String content = hit.getSourceAsMap().get("content").toString();
                    post.setContent(content);

                    String status = hit.getSourceAsMap().get("status").toString();
                    post.setStatus(Integer.valueOf(status));

                    String createTime = hit.getSourceAsMap().get("createTime").toString();
                    // createTime是一个Long类型的字符串
                    post.setCreateTime(new Date(Long.valueOf(createTime)));

                    String commentCount = hit.getSourceAsMap().get("commentCount").toString();
                    post.setCommentCount(Integer.valueOf(commentCount));

                    // 处理高亮显示的结果
                    // 获取高亮显示的内容
                    HighlightField titleField = hit.getHighlightFields().get("title");
                    if (titleField!=null){
                        // 覆盖之前的标题
                        // titleField.getFragments()返回一个数组，因为一段文本中可能有多个关键字
                        post.setTitle(titleField.getFragments()[0].toString());
                    }

                    HighlightField contentField = hit.getHighlightFields().get("content");
                    if (contentField != null) {
                        post.setContent(contentField.getFragments()[0].toString());
                    }

                    list.add(post);
                }

                // AggregatedPageImpl是AggregatedPage的实现类
                // [数据集合,pageable,记录总数,response.getAggregations(),response.getScrollId(), hits.getMaxScore()]
                return new AggregatedPageImpl(list, pageable,
                        hits.getTotalHits(), response.getAggregations(),
                        response.getScrollId(), hits.getMaxScore());
            }

            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                return null;
            }
        });
    }

}
